package com.bookseek.xreader.views.reader;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextPaint;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.blankj.utilcode.util.ScreenUtils;
import com.blankj.utilcode.util.SizeUtils;
import com.bookseek.xreader.R;
import com.bookseek.xreader.consts.XreaderConts;
import com.bookseek.xreader.params.BookParams;
import com.bookseek.xreader.params.BookSearchParams;
import com.bookseek.xreader.po.THisPo;
import com.bookseek.xreader.service.NbSqlQueryManager;
import com.bookseek.xreader.service.dk.FtsBookService;
import com.bookseek.xreader.service.dk.THisService;
import com.bookseek.xreader.utils.DLog;
import com.bookseek.xreader.utils.DxmItemClickListener;
import com.bookseek.xreader.utils.JkhlUtils;
import com.bookseek.xreader.utils.LocaleUtils;
import com.bookseek.xreader.utils.NbUtils;
import com.bookseek.xreader.utils.OkHttpUtils;
import com.bookseek.xreader.utils.WordTextUtils;
import com.bookseek.xreader.views.adapter.BookChapterItemAdapter;
import com.bookseek.xreader.views.adapter.BookReaderAdapter;
import com.bookseek.xreader.views.adapter.FtsBookSearchAdapter;
import com.bookseek.xreader.views.adapter.LastItemNoDividerDecoration;
import com.bookseek.xreader.views.helper.SearchViewHelper;
import com.bookseek.xreader.vo.FtsBookSearchVo;
import com.bookseek.xreader.vo.FtsBookVo;
import com.bookseek.xreader.vo.THisVo;
import com.bookseek.xreader.vo.TxtReadLineVo;
import com.bookseek.xreader.vo.mock.ChapterBcontDetailVo;
import com.bookseek.xreader.vo.mock.FanYiVo;
import com.bookseek.xreader.vo.mock.TBookChapterVo;
import com.bookseek.xreader.vo.mock.TBookInfoVo;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.slider.Slider;
import com.google.android.material.tabs.TabLayout;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import yuku.ambilwarna.AmbilWarnaDialog;


public class BookReaderActivity extends AppCompatActivity implements View.OnClickListener, DxmItemClickListener {


    private BookParams bookParams = new BookParams();

    private TBookInfoVo bookInfo;

    private RecyclerView txtReaderRv;//阅读区域

    private BookReaderAdapter adapter;//内容阅读区域

    private BookChapterItemAdapter chapterItemAdapter;

    private RecyclerView chaptersRv;

    private ConstraintLayout chapterSearchTextDiv,chapterDescTextDiv;

    private LinearLayoutCompat optNavMenu; //底部操作栏-默认隐藏

    private ImageButton icon_menu_btn; //章节目录操作按钮
    private ImageButton icon_voice_btn; //音频
    private ImageButton icon_brightness_btn; //背景
    private ImageButton icon_setting_btn; //设置

    private ConstraintLayout chapterPanelLayout;
    private ConstraintLayout voicePanelLayout;
    private ConstraintLayout brightnessPanelLayout;
    private ConstraintLayout settingPanelLayout;

    private Map<ImageButton,View> bottomNavMenuMap = new HashMap<>();

    private MaterialButton readBgColor00,readBgColor01,readBgColor02,readBgColor03;
    private Map<MaterialButton, Integer> materialButtonDrawableMap = new HashMap<>();

    private float fontSizeWenSp = 20;//单位 sp
    private float fontSizeYiSp = 14;//单位 sp

    private int wenColor = 0;
    private int yiColor = 0;

    private int nowThemeColor = R.drawable.read_bg_01;

    private float readTextPaddingDp = 16.0f;//水平padding总和

    private int firstCompletelyVisibleItemPosition = 0;

    private Slider fontWenSizeSlider,fontYiSizeSlider;

    private Switch yiwenSwitch;

    private boolean showYiwen = true;

    private int fontType = 0;//0=简体 1=港 2=台
    private RadioGroup fontTypeRadioGroup;//繁体字设置

    private MaterialButton preChapterBtn,nextChapterBtn;

    private ProgressBar pb_loading;

    private MaterialButton wenColorBtn,yiColorBtn,defaultWenYiColorBtn;

    private TabLayout chapterNavTab;

    private SearchViewHelper searchHelper;

    private List<FtsBookVo> ftsBookVos;

    private BookSearchParams bookSearchParams = new BookSearchParams();

    private FtsBookSearchVo ftsBookSearchVo = new FtsBookSearchVo();

    private RecyclerView bookFullTextRv;

    private boolean isLoading = false;//搜全书状态

    private boolean hasMoreData = true;//搜全书标记是否还有更多数据

    private FtsBookSearchAdapter ftsBookSearchAdapter;

    private TextView fullSearchTv;

    private Handler handler;

    private Runnable fullSearchRunnable;

    private static final long DEBOUNCE_DELAY = 500;//单位毫秒

    private TextView bookDescTv;//书籍简介Tv


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_book_reader);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.book_main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        handler = new Handler(Looper.getMainLooper());

        initUiView();
        initParams();
    }


    private Integer getNowChapterIdIndex(){
        if(bookInfo != null && bookInfo.chapters != null && bookInfo.nowReadChapterId != null){
            int nowIndex = 0;
            for(int index = 0 ;index < bookInfo.chapters.size();index++){
                if(bookInfo.nowReadChapterId.equals(bookInfo.chapters.get(index).id)){
                    nowIndex = index;
                    break;
                }
            }
            return nowIndex;
        }

        return null;
    }
    private Long getPreChapterId(){
       Integer nowChapterIndex = getNowChapterIdIndex();
       if(nowChapterIndex != null && nowChapterIndex -1 >= 0){
           return bookInfo.chapters.get(nowChapterIndex-1).id;
       }
       return null;
    }

    private Long getNextChapterId(){
        Integer nowChapterIndex = getNowChapterIdIndex();
        if(nowChapterIndex != null && (nowChapterIndex +1) < bookInfo.chapters.size()){
            return bookInfo.chapters.get(nowChapterIndex+1).id;
        }
        return null;
    }

    private void detectItemPositions(RecyclerView recyclerView) {
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        if (layoutManager == null) return;

        // 获取第一个可见Item的位置
        int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();

        // 获取最后一个可见Item的位置
        int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();

        // 获取第一个完全可见Item的位置
        firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();

        // 获取最后一个完全可见Item的位置
        int lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();

        DLog.info("Scroll-A", "firstVisibleItemPosition: " + firstVisibleItemPosition,adapter.getDataItemJson(firstVisibleItemPosition));
        DLog.info("Scroll-A", "lastVisibleItemPosition: " + lastVisibleItemPosition,adapter.getDataItemJson(lastVisibleItemPosition));

        DLog.info("Scroll-B", "firstCompletelyVisibleItemPosition: " + firstCompletelyVisibleItemPosition,adapter.getDataItemJson(firstCompletelyVisibleItemPosition));
        DLog.info("Scroll-B", "lastCompletelyVisibleItemPosition: " + lastCompletelyVisibleItemPosition,adapter.getDataItemJson(lastCompletelyVisibleItemPosition));

    }

    private void saveTHisInfo(){
        //保存当前阅读位置信息
        TxtReadLineVo lineVo = adapter.getFirstVisitWenInfo(firstCompletelyVisibleItemPosition);
        THisPo po = new THisPo();
        po.bid = bookInfo.id;
        po.cid = bookInfo.nowReadChapterId;
        po.bauthor = bookInfo.bauthor;
        po.bchao = bookInfo.bchao;
        TBookChapterVo chapterVo = getChapterById(po.cid);
        if(chapterVo != null){
            po.cname = chapterVo.btitle;
        }
        if(lineVo != null){
            po.pindex = lineVo.pindex;
            po.pstart = lineVo.start;
            po.pend = lineVo.end;
            DLog.info("阅读位置信息===》",OkHttpUtils.gson.toJson(po));
            NbSqlQueryManager.getInstance().saveDianjiReadInfo(po);
        }
    }
    private void onScrollTxtReaderRv(){
        if(txtReaderRv != null){
            txtReaderRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    // 滚动状态变化时触发
                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                        // 滚动停止
                        DLog.info("Scroll", "滚动已停止");
                        saveTHisInfo();
                        //将阅读位置记录到DB
                    } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                        // 正在被拖拽滚动
                        DLog.info("正在被拖拽滚动==>");
                    } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
                        // 惯性滚动
                        DLog.info("惯性滚动==>");
                    }
                }

                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    detectItemPositions(recyclerView);
                }
            });
        }
    }

    private void setChapterBtnEnableColor(MaterialButton btn){
        if(btn != null){
            int color =ContextCompat.getColor(this,btn.isEnabled() ? R.color.layui_class_blue_color : R.color.layui_gray_04);
            btn.setTextColor(color);
        }
    }
    private void updatehChapterBtnStatus(){
        preChapterBtn.setEnabled(getPreChapterId() != null);
        setChapterBtnEnableColor(preChapterBtn);
        nextChapterBtn.setEnabled(getNextChapterId() != null);
        setChapterBtnEnableColor(nextChapterBtn);
    }
    private void initChapterChange(){
        preChapterBtn = findViewById(R.id.preChapterBtn);
        nextChapterBtn = findViewById(R.id.nextChapterBtn);
        preChapterBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loadChapterContentByChapterId(getPreChapterId());
            }
        });
        nextChapterBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loadChapterContentByChapterId(getNextChapterId());
            }
        });
    }
    private void initFontTypeRadioGroup(){
        fontTypeRadioGroup = findViewById(R.id.fontTypeRadioGroup);
        if(fontType == 0){
            fontTypeRadioGroup.check(R.id.fontTypeRbJian);
        }else if(fontType == 1){
            fontTypeRadioGroup.check(R.id.fontTypeRbGang);
        }else if(fontType == 2){
            fontTypeRadioGroup.check(R.id.fontTypeRbTai);
        }
        fontTypeRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
                if(checkedId == R.id.fontTypeRbJian){
                    fontType =0;
                }else if(checkedId == R.id.fontTypeRbGang){
                    fontType = 1;
                }else if(checkedId == R.id.fontTypeRbTai){
                    fontType = 2;
                }
                NbUtils.setFontZhType(fontType);
                loadChapterContentByChapterId(bookInfo.nowReadChapterId);
            }
        });
    }
    private void initFontSliver(){
        //正文字体
        fontWenSizeSlider = findViewById(R.id.fontWenSizeSlider);
        fontWenSizeSlider.setValue(fontSizeWenSp);
        fontWenSizeSlider.addOnSliderTouchListener(new Slider.OnSliderTouchListener() {
            @SuppressLint("RestrictedApi")
            @Override
            public void onStartTrackingTouch(Slider slider) {
                // 开始滑动时触发（手指按下滑块）
            }

            @SuppressLint("RestrictedApi")
            @Override
            public void onStopTrackingTouch(Slider slider) {
                // 结束滑动时触发（手指离开滑块）
                float finalValue = slider.getValue();
                fontSizeWenSp = finalValue;
                NbUtils.setFontWenSizeSp(fontSizeWenSp);
                adapter.updateFontSize(fontSizeWenSp,fontSizeYiSp);
                loadChapterContentByChapterId(bookInfo.nowReadChapterId);
                DLog.info("finalValue==>",finalValue);
            }
        });


        //译文字体
        fontYiSizeSlider = findViewById(R.id.fontYiSizeSlider);
        fontYiSizeSlider.setValue(fontSizeYiSp);
        fontYiSizeSlider.addOnSliderTouchListener(new Slider.OnSliderTouchListener() {
            @SuppressLint("RestrictedApi")
            @Override
            public void onStartTrackingTouch(Slider slider) {
                // 开始滑动时触发（手指按下滑块）
            }

            @SuppressLint("RestrictedApi")
            @Override
            public void onStopTrackingTouch(Slider slider) {
                // 结束滑动时触发（手指离开滑块）
                float finalValue = slider.getValue();
                fontSizeYiSp = finalValue;
                NbUtils.setFontYiSizeSp(fontSizeYiSp);
                adapter.updateFontSize(fontSizeWenSp,fontSizeYiSp);
                loadChapterContentByChapterId(bookInfo.nowReadChapterId);
                DLog.info("finalValue==>",finalValue);
            }
        });


    }

    /***
     * 加载默认配置
     */
    private void initSettings(){
            fontSizeWenSp = NbUtils.getFontWenSizeSp();
            fontSizeYiSp = NbUtils.getFontYiSizeSp();
            showYiwen = NbUtils.getShowYiwen();
            fontType = NbUtils.getFontZhType();//繁体类型
            DLog.info("fontSizeWenSp 默认配置===》",fontSizeWenSp,"==>fontSizeYiSp=",fontSizeYiSp,"showYiwen=>",showYiwen);

    }

    /**
     * 是否显示译文
     */
    private void initYiwenSwitch(){
        yiwenSwitch = findViewById(R.id.yiwenSwitch);
        yiwenSwitch.setChecked(showYiwen);
        yiwenSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                showYiwen = isChecked;
                NbUtils.setShowYiwen(showYiwen);
                loadChapterContentByChapterId(bookInfo.nowReadChapterId);
            }
        });
    }

    /**
     * 初始化页面元素
     */
    private void initUiView(){
        //设置默认配置参数
        initSettings();
        //加载等待框
        pb_loading = findViewById(R.id.pb_loading);
        txtReaderRv = findViewById(R.id.txtReaderRv);
        adapter = new BookReaderAdapter(new ArrayList<>(),this,getApplicationContext());
        LinearLayoutManager layoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
        txtReaderRv.setLayoutManager(layoutManager);
        txtReaderRv.setAdapter(adapter);
        adapter.updateFontSize(fontSizeWenSp,fontSizeYiSp);
        onScrollTxtReaderRv();//监听滚动
        optNavMenu = findViewById(R.id.optNavMenu);
        icon_menu_btn = findViewById(R.id.icon_menu_btn);
        icon_voice_btn = findViewById(R.id.icon_voice_btn);
        icon_brightness_btn = findViewById(R.id.icon_brightness_btn);
        icon_setting_btn = findViewById(R.id.icon_setting_btn);
        chapterPanelLayout = findViewById(R.id.chapterPanel);
        voicePanelLayout = findViewById(R.id.voicePanel);
        brightnessPanelLayout = findViewById(R.id.brightnessPanel);
        settingPanelLayout = findViewById(R.id.settingPanel);
        txtReaderRv.setOnClickListener(this);
        icon_menu_btn.setOnClickListener(this);
        icon_voice_btn.setOnClickListener(this);
        icon_brightness_btn.setOnClickListener(this);
        icon_setting_btn.setOnClickListener(this);
        bottomNavMenuMap.put(icon_menu_btn,chapterPanelLayout);
        bottomNavMenuMap.put(icon_voice_btn,voicePanelLayout);
        bottomNavMenuMap.put(icon_brightness_btn,brightnessPanelLayout);
        bottomNavMenuMap.put(icon_setting_btn,settingPanelLayout);
        chapterPanelLayout.setOnClickListener(this);
        voicePanelLayout.setOnClickListener(this);
        brightnessPanelLayout.setOnClickListener(this);
        settingPanelLayout.setOnClickListener(this);

        //章节面板区域
        chaptersRv = findViewById(R.id.chaptersRv);
        chapterItemAdapter = new BookChapterItemAdapter(null,this);
        LinearLayoutManager chapterItemLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
        chaptersRv.setLayoutManager(chapterItemLayoutManager);
        chaptersRv.setAdapter(chapterItemAdapter);
        chaptersRv.addItemDecoration(new LastItemNoDividerDecoration(
                this,
                XreaderConts.DIVIDER_COLOR,  // 分割线颜色
                R.dimen.divider_height                           // 分割线高度（在dimens.xml中定义）
        ));

        //背景面板区域
        readBgColor00 = findViewById(R.id.readBgColor00);
        readBgColor01 = findViewById(R.id.readBgColor01);
        readBgColor02 = findViewById(R.id.readBgColor02);
        readBgColor03 = findViewById(R.id.readBgColor03);
        readBgColor00.setOnClickListener(this);
        readBgColor01.setOnClickListener(this);
        readBgColor02.setOnClickListener(this);
        readBgColor03.setOnClickListener(this);
        materialButtonDrawableMap.put(readBgColor00,R.drawable.read_bg_00);
        materialButtonDrawableMap.put(readBgColor01,R.drawable.read_bg_01);
        materialButtonDrawableMap.put(readBgColor02,R.drawable.read_bg_02);

        wenColorBtn = findViewById(R.id.wenColorBtn);
        yiColorBtn = findViewById(R.id.yiColorBtn);
        defaultWenYiColorBtn = findViewById(R.id.defaultWenYiColorBtn);
        wenColorBtn.setOnClickListener(this);
        yiColorBtn.setOnClickListener(this);
        defaultWenYiColorBtn.setOnClickListener(this);

        initFontSliver();
        initYiwenSwitch();
        initFontTypeRadioGroup();
        initChapterChange();
        initReadColorSetting();//背景、字体颜色设置
        yiColorBtn.setTextColor(yiColor);
        wenColorBtn.setTextColor(wenColor);
        initChapterNavTab();
        //chapterSearchTextDiv,chapterDescTextDiv
        chapterSearchTextDiv = findViewById(R.id.chapterSearchTextDiv);
        chapterDescTextDiv = findViewById(R.id.chapterDescTextDiv);
        initSearchFullBook();

    }

    private void loadBookFullSearch(boolean append){
        Context ctx = this;
        if(ftsBookVos == null){
            fullSearchTv.setText("首次加载较慢,请稍后...");
            fullSearchTv.setTextColor(ContextCompat.getColor(ctx,R.color.my_light_color_v1));
            fullSearchTv.setVisibility(View.VISIBLE);
        }
        if(JkhlUtils.isBlank(bookSearchParams.keyword)){
            fullSearchTv.setVisibility(View.GONE);
        }
        OkHttpUtils.getInstance().getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                FtsBookSearchVo  searchResultVo =  queryBookKeywords(bookSearchParams);
                //更新章节id位置
                bookSearchParams.offsetCid = searchResultVo.offsetCid;
                //切换到主线程更新UI
                OkHttpUtils.getInstance().getMainHandler().post(()->{
                    List<TxtReadLineVo> lines = searchResultVo.lines;
                    //区分追加还是
                    ftsBookSearchAdapter.updateDataLines(searchResultVo.lines,append);
                    hasMoreData = !lines.isEmpty();
                    if(!lines.isEmpty()){
                        fullSearchTv.setVisibility(View.GONE);
                    }else{
                        if(!JkhlUtils.isBlank(bookSearchParams.keyword)){
                            fullSearchTv.setVisibility(View.VISIBLE);
                            fullSearchTv.setText(ftsBookSearchAdapter.getItemCount() == 0 ? "未查到相关结果,可切换关键词重试" : "");
                            fullSearchTv.setTextColor(ContextCompat.getColor(ctx,R.color.my_red));
                        }
                    }
                });
                isLoading = false;
            }
        });
    }

    /**
     * 初始化全书搜索区域
     */
    private void initSearchFullBook(){
        //文本变化时自动触发搜索
        fullSearchRunnable = ()->{
            loadBookFullSearch(false);
        };
        //fullSearchTv
        fullSearchTv = findViewById(R.id.fullSearchTv);//提示加载
        //初始化列表
        bookFullTextRv = findViewById(R.id.bookFullTextRv);
        bookDescTv = findViewById(R.id.bookDescTv);
        ftsBookSearchAdapter = new FtsBookSearchAdapter(ftsBookSearchVo, new DxmItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                DLog.info("点击了章节====》",position,OkHttpUtils.gson.toJson(ftsBookSearchAdapter.getPositionItem(position)));
                //跳转到章节阅读页面，根据 cid,start,end 定位滚动位置+关键词高亮
                TxtReadLineVo lineVo = ftsBookSearchAdapter.getPositionItem(position);
                clickChapterItem(0,lineVo);
            }

            @Override
            public void onItemLongClick(View view, int position) {

            }
        });
        LinearLayoutManager chapterItemLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
        bookFullTextRv.setLayoutManager(chapterItemLayoutManager);
        bookFullTextRv.setAdapter(ftsBookSearchAdapter);
        //上拉加载更多
        bookFullTextRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                // 只处理向上滚动
                if (dy <= 0) return;

                int visibleItemCount = chapterItemLayoutManager.getChildCount();
                int totalItemCount = chapterItemLayoutManager.getItemCount();
                int firstVisibleItemPosition = chapterItemLayoutManager.findFirstVisibleItemPosition();

                if (!isLoading && hasMoreData && (visibleItemCount + firstVisibleItemPosition) >= totalItemCount
                        && firstVisibleItemPosition >= 0) {
                    isLoading = true;
                    DLog.info("查询更多---》 offsetCid",bookSearchParams.offsetCid);
                    bookFullTextRv.post(()->{
                       loadBookFullSearch(true);
                    });
                }
            }
        });
        ImageView clearButton = findViewById(R.id.iv_clear);
        // 初始化搜索栏组件
        EditText searchEditText = findViewById(R.id.et_search);
        // 创建搜索助手
        searchHelper = new SearchViewHelper(this, searchEditText, clearButton);
        // 设置搜索监听
        searchHelper.setOnSearchListener(new SearchViewHelper.OnSearchListener() {
            @Override
            public void onSearch(String query) {
                // 处理搜索逻辑
                Toast.makeText(BookReaderActivity.this, "搜索: " + query, Toast.LENGTH_SHORT).show();
                bookSearchParams.offsetCid = 0L;
                loadBookFullSearch(false);
            }
            @Override
            public void onTextChanged(String text) {
                // 处理文本变化（如实时搜索建议）
                // Log.d("Search", "文本变化: " + text);
                DLog.info("Search:","文本变化: " + text);
                bookSearchParams.keyword = text;
                bookSearchParams.offsetCid = 0L;
                handler.removeCallbacks(fullSearchRunnable);
                handler.postDelayed(fullSearchRunnable, DEBOUNCE_DELAY);
            }
        });
    }


    private void tabSelectedStatus(int position,int visibility){
        if(position == 0){
            chaptersRv.setVisibility(visibility);
        }else if(position == 1){
            chapterSearchTextDiv.setVisibility(visibility);
        }else if(position == 2){
            chapterDescTextDiv.setVisibility(visibility);
        }

    }
    private void initChapterNavTab(){
        //初始化 章节TabNav 导航区域
        chapterNavTab = findViewById(R.id.chapterNavTab);
        // 1. 动态添加标签（也可在 XML 中通过 <TabItem> 静态添加）
        chapterNavTab.addTab(chapterNavTab.newTab().setText("章节"));
        chapterNavTab.addTab(chapterNavTab.newTab().setText("搜全书"));
        chapterNavTab.addTab(chapterNavTab.newTab().setText("简介"));
        // 可选：添加带图标的标签
        // tabLayout.addTab(tabLayout.newTab().setText("消息").setIcon(R.drawable.ic_message));

        // 2. 设置标签选中监听器，处理切换逻辑
        chapterNavTab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                // 标签被选中时回调（处理核心逻辑）
                int position = tab.getPosition(); // 获取标签位置
                DLog.info("选中标签===》",position,tab.getText());
                tabSelectedStatus(position,View.VISIBLE);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                // 标签从选中变为未选中时回调（可选处理）
                int position = tab.getPosition(); // 获取标签位置
                tabSelectedStatus(position,View.INVISIBLE);
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                // 标签已选中但再次被点击时回调（可选处理，如刷新内容）
            }
        });

        // 初始默认选中第一个标签
//        if (tabLayout.getTabCount() > 0) {
//            tabLayout.getTabAt(0).select();
//        }
    }

    private void initScrollChapterItem(){
        LinearLayoutManager layoutManager = (LinearLayoutManager) chaptersRv.getLayoutManager();
        if(layoutManager != null && bookInfo.nowReadChapterId != null){
           int pos = chapterItemAdapter.getChapterPos(bookInfo.nowReadChapterId);
           layoutManager.scrollToPositionWithOffset(pos,0);
        }
    }
    private void initParams(){
        Intent intent = getIntent();
        Long cid = intent.getLongExtra("bookid",-1L);
        bookParams.cid = cid;
        DLog.info("bookParams=>", OkHttpUtils.gson.toJson(bookParams));
        //根据书籍id获取章节、历史信息
        NbSqlQueryManager.getInstance().getTBookInfoAsync(bookParams, new OkHttpUtils.SqlCallback<TBookInfoVo>() {
            @Override
            public void onSuccess(TBookInfoVo result) {
                bookInfo = result;
                //加载UI
                //章节目录设置
                chapterItemAdapter.updateData(bookInfo);
                //书籍简介
                bookDescTv.setText(JkhlUtils.toStr(bookInfo.bdesc));
                //加载章节信息
                DLog.info("nowReadChapterId==>",bookInfo.nowReadChapterId,bookInfo.chapters != null );

                loadChapterContentByChapterId(bookInfo.nowReadChapterId);
                //首次滚动章节列表到可视区域
                initScrollChapterItem();
            }
            @Override
            public void onError(Exception e) {
                if(e != null){
                    DLog.info("error ==>",e.getMessage());
                }
            }
        });
    }


    private void clickTxtReaderRv(){
        //判断
        DLog.info("===========>点击了 阅读区域");
        if(optNavMenu != null){
                toggleViewVisibility(optNavMenu);
                if(optNavMenu.getVisibility() == View.GONE){
                    bottomNavMenuMap.forEach((iconBtn,iconView)->{
                        iconBtn.setSelected(false);
                        iconView.setVisibility(View.GONE);
                    });
                }
        }
    }

    private void toggleViewVisibility(View view){
        if(view == null){
            return;
        }
        if(view.getVisibility() == View.GONE){
            view.setVisibility(View.VISIBLE);
        }else if(view.getVisibility() ==  View.VISIBLE){
            view.setVisibility(View.GONE);
        }

    }

    private void clickIconMenuBtn(ImageButton ibtn){
        //隐藏其他操作菜单关联得面板
        Set<ImageButton> btns = bottomNavMenuMap.keySet();
        for(ImageButton btn : btns){
            View btnView = bottomNavMenuMap.get(btn);
            if(btn == ibtn){
                toggleViewVisibility(btnView);
                btn.setSelected(btnView.getVisibility() == View.VISIBLE);
            }else{
                btnView.setVisibility(View.GONE);
                btn.setSelected(false);
            }
        }

    }

    public void initReadColorSetting(){
        Integer resId = NbUtils.getReaderBgColor();
        yiColor = NbUtils.getFontColorYi();
        if(resId != null && resId != 0){
            Drawable imageDrawable = ContextCompat.getDrawable(this, resId);
            txtReaderRv.setBackground(imageDrawable);
            nowThemeColor = resId;
            wenColor = NbUtils.getFontColorWen();
        }
        if(resId == 0){
            //黑色背景
            txtReaderRv.setBackgroundColor(ContextCompat.getColor(this,R.color.layui_dark_04));
            nowThemeColor =0;
            wenColor = XreaderConts.WY_TYPE_WEN_DEFAULT_WHITE_COLOR;
        }
    }
    public void clickBrightBtn(MaterialButton btn){
        Integer resId = materialButtonDrawableMap.get(btn);
        materialButtonDrawableMap.keySet().forEach(d->{
            d.setSelected(d == btn);
        });
        if(resId != null){
            if(resId == nowThemeColor){
                return;
            }
            boolean needReload = nowThemeColor == 0;
            Drawable imageDrawable = ContextCompat.getDrawable(this, resId);
            txtReaderRv.setBackground(imageDrawable);
            NbUtils.setReaderBgColor(resId);
            nowThemeColor = resId;
            wenColor = NbUtils.getFontColorWen();
            yiColor = NbUtils.getFontColorYi();
            //重新加载数据
            if(needReload){
                //initParams();
                adapter.updateFontColor(wenColor,yiColor);
            }
        }else{
            //重复
            if(nowThemeColor == 0){
                return;
            }
            //黑色背景 白色字体 特殊处理
            txtReaderRv.setBackgroundColor(ContextCompat.getColor(this,R.color.layui_dark_04));
            wenColor = XreaderConts.WY_TYPE_WEN_DEFAULT_WHITE_COLOR;
            NbUtils.setReaderBgColor(0);
            nowThemeColor = 0;
            //重新加载数据
            //initParams();
            adapter.updateFontColor(wenColor,yiColor);
        }
    }

    /**
     *
     * @param view
     * @param type 1=正文 2=译文
     */
    private void pickFontColor(View view,int type){
        String title = type == 1 ? "正文颜色选则" : "译文颜色选择";
        //
        // 显示系统颜色选择器
        DLog.info(title);
        AmbilWarnaDialog dialog = new AmbilWarnaDialog(this, type == 1 ? wenColor : yiColor, new AmbilWarnaDialog.OnAmbilWarnaListener() {
            @Override
            public void onOk(AmbilWarnaDialog dialog, int color) {
                // color is the color selected by the user.
                DLog.info("color==>",color);
                if(type == 1){
                    wenColor = color;
                    updateWenYiColor(wenColor,yiColor);
                }else{
                    yiColor = color;
                    updateWenYiColor(wenColor,yiColor);
                }
            }
            @Override
            public void onCancel(AmbilWarnaDialog dialog) {

            }
        });
        dialog.show();
        Button okBtn = dialog.getDialog().getButton(AlertDialog.BUTTON_POSITIVE);
        if(okBtn != null){
            okBtn.setText("确定");
            okBtn.setTextColor(ContextCompat.getColor(this,R.color.button_normal_color));
        }
        Button cancelBtn = dialog.getDialog().getButton(AlertDialog.BUTTON_NEGATIVE);
        if(cancelBtn != null){
            cancelBtn.setText("取消");
            cancelBtn.setTextColor(ContextCompat.getColor(this,R.color.layui_gray_07));
        }
    }


    private void clickWenColorBtn(View view){
        pickFontColor(view,1);
    }

    private void clickYiColorBtn(View view){
        pickFontColor(view,0);
    }
    private void clifkDefaultWenYiColorBtn(View view){
        updateWenYiColor(XreaderConts.WY_TYPE_WEN_DEFAULT_COLOR,XreaderConts.WY_TYPE_YI_DEFAULT_COLOR);
    }

    private void updateWenYiColor(int wenColorVal,int yiColorVal){
        wenColor = wenColorVal;
        yiColor = yiColorVal;
        NbUtils.setFontColorWen(wenColor);
        NbUtils.setFontColorYi(yiColor);
        adapter.updateFontColor(wenColor,yiColor);
        wenColorBtn.setTextColor(wenColor);
        yiColorBtn.setTextColor(yiColor);
    }
    /**
     * 点击事件入口
     * @param view
     */
    @Override
    public void onClick(View view) {
        DLog.info("============>点击事件========》",view.getId(),"txtReaderRv.getId",txtReaderRv.getId(),view == txtReaderRv,R.id.txtLineLlc);
        //点击 txtReaderRv 或 txtReaderRv 里面得item项，都会打开底部操作栏
        if(view == txtReaderRv || view.getId() == R.id.txtLineLlc){
            clickTxtReaderRv();
        }else if(view == icon_menu_btn || view == icon_brightness_btn || view == icon_voice_btn || view == icon_setting_btn){
            //底部操作按钮操作
            clickIconMenuBtn((ImageButton) view);

        }else if(view == readBgColor00 || view == readBgColor01 || view == readBgColor02 || view == readBgColor03){
            clickBrightBtn((MaterialButton) view);
        }else if(view == wenColorBtn){
            clickWenColorBtn(view);
        }else if(view == yiColorBtn){
            clickYiColorBtn(view);
        }else if(view == defaultWenYiColorBtn){
            clifkDefaultWenYiColorBtn(view);
        }
    }



    private void loadChapterContentByIndex(int position){
        loadChapterContent(getChapterByIndex(position));
    }
    private void loadChapterContentByChapterId(Long chapterId){
        if(chapterId == null){
            return;
        }
        loadChapterContent(getChapterById(chapterId));

    }


    /**
     * 如果外部指定了 readHis信息，则以外部传入的为主
     * @param chapterVo
     * @param assignReadHisPo
     */
    private void loadChapterContentWithHis(TBookChapterVo chapterVo,THisPo assignReadHisPo){
        DLog.info("loadChapterContent==>",OkHttpUtils.gson.toJson(chapterVo));
        if(chapterVo == null){
            DLog.error("章节对象为空==》");
            return;
        }
        Long bookid = bookInfo.id;
        Long chapterId = chapterVo.id;
        bookInfo.nowReadChapterId = chapterId;//记录正在阅读的章节id
        updatehChapterBtnStatus();//更新章节切换按钮状态
        THisPo readHisPo = THisService.getInstance().getByBidAndCidPtype(bookid,chapterId,XreaderConts.TMETA_BTYPE_DIANJI);
        if(chapterVo.contentDetailVo != null){
            DLog.info("titlte=>",chapterVo.btitle," 已经加载过 ");
            chapterVo.contentDetailVo.readHisPo = assignReadHisPo != null ? assignReadHisPo : readHisPo;
            parseLineTxt(chapterVo.contentDetailVo);
            return;
        }
        //进度条
        pb_loading.setVisibility(View.VISIBLE);
        NbSqlQueryManager.getInstance().getTBookChapterContentAsync(chapterVo, new OkHttpUtils.SqlCallback<ChapterBcontDetailVo>() {
            @Override
            public void onSuccess(ChapterBcontDetailVo result) {
                DLog.info("titlte=>",chapterVo.btitle,"章节返回内容==》",OkHttpUtils.gson.toJson(result));
                chapterVo.contentDetailVo = result;
                chapterVo.contentDetailVo.readHisPo = assignReadHisPo != null ? assignReadHisPo : readHisPo;
                parseLineTxt(result);
                pb_loading.setVisibility(View.GONE);
            }

            @Override
            public void onError(Exception e) {
                e.printStackTrace();
                DLog.error("e=>",e.getMessage());
            }
        });

    }
    private void loadChapterContent(TBookChapterVo chapterVo){
        loadChapterContentWithHis(chapterVo,null);
    }
    private TBookChapterVo getChapterByIndex(int position){
        return  bookInfo != null && bookInfo.chapters != null && position < bookInfo.chapters.size() ? bookInfo.chapters.get(position) : null;
    }

    private TBookChapterVo getChapterById(Long chapterId){
        return  bookInfo != null &&
                bookInfo.chapters != null &&
                chapterId != null ?
                bookInfo.chapters.stream()
                        .filter(d->d.id != null && chapterId.equals(d.id))
                        .findFirst().orElse(null) :
                null;
    }
    public void clickChapterItem(int chapterItemPos,TxtReadLineVo fullSearchLine){
        int oldPos = chapterItemAdapter.getChapterPos(bookInfo.nowReadChapterId);
        clickTxtReaderRv();
        if(fullSearchLine != null){
            int newPos = chapterItemAdapter.getChapterPos(fullSearchLine.cid);
            //加载新的
            THisVo assignReadHisVo = new THisVo();
            assignReadHisVo.id = 0L;
            assignReadHisVo.cid = fullSearchLine.cid;
            assignReadHisVo.pindex = fullSearchLine.pindex;
            assignReadHisVo.pstart = fullSearchLine.start;
            assignReadHisVo.pend = fullSearchLine.end;
            assignReadHisVo.hightLightWord = bookSearchParams.keyword;
            loadChapterContentWithHis(getChapterByIndex(newPos),assignReadHisVo);
            chapterItemAdapter.notifyItemChanged(oldPos);
            chapterItemAdapter.notifyItemChanged(newPos);
        }else if(chapterItemPos >= 0){
            loadChapterContentByIndex(chapterItemPos);
            chapterItemAdapter.notifyItemChanged(oldPos);
            chapterItemAdapter.notifyItemChanged(chapterItemPos);
        }

    }
    /**
     * 点击chapter 时的回调
     * @param view
     * @param position
     */
    @Override
    public void onItemClick(View view, int position) {
        DLog.info("点击了item index=======>",position);
        clickChapterItem(position,null);
    }

    @Override
    public void onItemLongClick(View view, int position) {
        DLog.info("长按了item index=======>",position);
    }

    private List<TxtReadLineVo> parseLineV1(String allTexts,TextPaint textPaint,int validWidth){
        List<TxtReadLineVo> lines = new ArrayList<>();
        int totalCount = 0;
        int len = allTexts.length();
        String str = allTexts;
        int start = 0;
        for(;;){
            float[] measuredWidth = new float[1];
            int count = textPaint.breakText(str, true, validWidth, measuredWidth);
            TxtReadLineVo lineVo = new TxtReadLineVo();
            lineVo.start = start;
            lineVo.end = start+count;
            lineVo.validWidth = validWidth;
            lineVo.charWidth = measuredWidth[0];
            lineVo.txt = str.substring(0,count);
            lines.add(lineVo);
            start = lineVo.end;
            str = str.substring(count);
            totalCount += count;
            if(totalCount >= len){
                break;
            }
        }
        return lines;
    }
    public void parseLineTxt(ChapterBcontDetailVo detailVo){

        Long startTm = System.currentTimeMillis();
        THisPo assignReadHisPo = detailVo.readHisPo;
        String hightLightWord = "";
        Integer hightPindex = -1;

        if(assignReadHisPo instanceof THisVo){
            THisVo hisVo = (THisVo)assignReadHisPo;
            hightLightWord = hisVo.hightLightWord;
            hightPindex = hisVo.pindex != null ? hisVo.pindex : -1;
        }
        boolean  hasSearchWord = JkhlUtils.notBlank(hightLightWord);
        //正文画笔
        TextPaint textPaint = new TextPaint();
        int fontSizePx = SizeUtils.sp2px(fontSizeWenSp);
        textPaint.setTextSize(fontSizePx);

        //译文
        TextPaint yiTextPaint = new TextPaint();
        int yiFontSizePx = SizeUtils.sp2px(fontSizeYiSp);
        yiTextPaint.setTextSize(yiFontSizePx);

        //RecyclerView 水平padding
        int padding = SizeUtils.dp2px(readTextPaddingDp);
        int validWidth = ScreenUtils.getScreenWidth() - padding;//可绘制的有效宽度
        List<TxtReadLineVo> allLines = new ArrayList<>();
        List<FanYiVo> conts = detailVo.bcontList;//章节段落
        int len = 0;
        for(int i = 0;i<conts.size();i++){
            FanYiVo fanYiVo = conts.get(i);
            //正文
            String x =LocaleUtils.toMainLandSimple(JkhlUtils.toStr(fanYiVo.x));
            if(fontType == 1){
                //港
                x = LocaleUtils.toHkZhText(x);
            }else if(fontType == 2){
                x = LocaleUtils.toTwZhText(x);
            }
            len += x.length();
            //x 为段落内容
            List<TxtReadLineVo> pLines = parseLineV1(x,textPaint,validWidth);
            Integer hightStartIndex = -1;
            Integer hightEndIndex = -1;
            if(hightPindex.equals(i) && hasSearchWord){
                //段落里关键词高亮显示
                hightStartIndex  = x.indexOf(hightLightWord);
                hightEndIndex = hightStartIndex+hightLightWord.length();
            }
            for(TxtReadLineVo pLine : pLines){
                pLine.pindex = i;
                pLine.wy = XreaderConts.WY_TYPE_WEN;
                pLine.color = wenColor;//正文颜色
                if(pLine.start != null && pLine.end != null && hightStartIndex >= 0){
                    for(int cIndex = pLine.start;cIndex < pLine.end;cIndex++){
                        if(cIndex >= hightStartIndex && cIndex < hightEndIndex ){
                            if(pLine.colorStart == null){
                                pLine.colorStart = cIndex;
                                pLine.colorEnd = pLine.colorStart+1;
                            }else{
                                pLine.colorEnd = cIndex+1;
                            }
                        }
                    }
                }
            }
            allLines.addAll(pLines);
            //是否要显示译文
            if(showYiwen){
                String y = JkhlUtils.toStr(fanYiVo.y);
                if(fontType == 1){
                    //港
                    y = LocaleUtils.toHkZhText(y);
                }else if(fontType == 2){
                    y = LocaleUtils.toTwZhText(y);
                }
                if(JkhlUtils.notBlank(y)){
                    List<TxtReadLineVo> yPLines = parseLineV1(y,yiTextPaint,validWidth);
                    for(TxtReadLineVo pLine : yPLines){
                        pLine.pindex = i;
                        pLine.wy = XreaderConts.WY_TYPE_YI;
                        pLine.color = yiColor;//译文颜色
                    }
                    allLines.addAll(yPLines);
                }
            }
        }
        Long endTm = System.currentTimeMillis();

        for(TxtReadLineVo lineVo : allLines){
            DLog.info("pindex=",lineVo.pindex,"start=",lineVo.start,"end=",lineVo.end,lineVo.txt," 字数=",lineVo.txt.length(),"charWidth=",lineVo.charWidth," validWidth=",lineVo.validWidth);
        }
        DLog.info("字数：",len," 测量耗时==》",endTm-startTm," ms");
        adapter.updateDatas(allLines);

        //        DLog.info("滚动到 index=23 的位置");
        LinearLayoutManager layoutManager = (LinearLayoutManager) txtReaderRv.getLayoutManager();
        if (layoutManager != null) {
            // 第一个参数：目标item的position
            // 第二个参数：item顶部与RecyclerView顶部的偏移量（0表示完全置顶）
            THisPo readHisPo = detailVo.readHisPo;
            int itemPosition = 0;
            if(readHisPo != null){
                Integer pindex = readHisPo.pindex;
                Integer pstart = readHisPo.pstart;
                Integer pend = readHisPo.pend;
                if(pindex != null && pstart != null && pend != null){
                    //正文
                    for(int lineIndex = 0;lineIndex < allLines.size();lineIndex++){
                        TxtReadLineVo lineInfo = allLines.get(lineIndex);
                        Integer start = lineInfo.start;
                        Integer end = lineInfo.end;
                        Integer dataIndex = lineInfo.pindex;
                        if(dataIndex != null && dataIndex.equals(pindex)){
                            itemPosition = lineIndex;
                            //[start,end] 和 [pstart,pend] 区间有交集
                            if( start <= pstart && pstart <= end){
                                break;
                            }
                            if(start >= pstart && start <= pend){
                                break;
                            }
                        }
                    }
                }
                DLog.info("历史阅读位置=pindex=",pindex,"pstart=",pstart,"pend=",pend,"pos=",itemPosition,OkHttpUtils.gson.toJson(allLines.get(itemPosition)));
            }
            DLog.info("滚动item位置====》",itemPosition);
            layoutManager.scrollToPositionWithOffset(itemPosition, 0);
        }
    }

    /**
     * 初始化书籍离线数据
     */
    private void initBoolLocalData(){
        if(ftsBookVos == null){
            Long bookid = bookParams.cid;
            ftsBookVos = FtsBookService.getInstance()
                    .getBookAllChapter(bookid).stream().sorted(Comparator.comparing(FtsBookVo::getCid,Comparator.nullsLast(Comparator.naturalOrder())))
                    .collect(Collectors.toList());
        }
    }

    public List<TxtReadLineVo> parseBookChapterFtsLinesPindex(Pattern pa,List<FanYiVo> bcontList){
        Set<Character> chars = new HashSet<>();
        chars.add(',');
        chars.add('，');
        chars.add('？');
        chars.add('?');
        chars.add(';');
        chars.add('.');
        chars.add('。');
        chars.add('!');
        chars.add('！');
        List<TxtReadLineVo> results = new ArrayList<>();
        for(int pindex = 0;pindex < bcontList.size();pindex++){
            String line = JkhlUtils.toStr(bcontList.get(pindex).x);
            List<TxtReadLineVo> pLines = parseBookFtsLines(pa,line,chars);
            for(TxtReadLineVo pLine : pLines){
                pLine.pindex = pindex;//设置段落号
            }
            results.addAll(pLines);
        }
        return results;
    }
    public List<TxtReadLineVo> parseBookFtsLines(Pattern pa,String line, Set<Character> chars){
        List<TxtReadLineVo> lineVos = new ArrayList<>();
        Matcher matcher = pa.matcher(line);
        int maxSubLen = 30;//20
        int maxCharView = 5;//4
        int strLen = line.length();
        while (matcher.find()){
            int start = matcher.start();
            int end = matcher.end();
            int preStart = start;
            int startSentenceNum = 0;
            int startMaxCharView = end >= strLen-10 ? maxCharView*2 : maxCharView;
            int startMaxSubLen = end>= strLen-10 ? maxSubLen*2 : maxSubLen;
            for(;preStart > 0 && (start-preStart) < startMaxSubLen && startSentenceNum <= startMaxCharView;preStart--){
                if(chars.contains(line.charAt(preStart))){
                    startSentenceNum++;
                }
            }
            if(chars.contains(line.charAt(preStart))){
                preStart++;
            }
            int endMaxCharView = start == 0 ? maxCharView*2 : maxCharView;
            int endMaxSubLen = start == 0 ? maxSubLen*2 : maxSubLen;
            int endSentenceNum = 0;
            int nextEnd = end;

            for(;nextEnd < strLen && (nextEnd-end) < endMaxSubLen && endSentenceNum <= endMaxCharView;nextEnd++){
                if(chars.contains(line.charAt(nextEnd))){
                    endSentenceNum++;
                }
            }
            TxtReadLineVo readLineVo = new TxtReadLineVo();
            readLineVo.start= preStart;//章节内容开始位置
            readLineVo.end = nextEnd;//章节内容结束位置
            readLineVo.colorStart = start-preStart;//截取文本-关键词高亮开始位置
            readLineVo.colorEnd = readLineVo.colorStart+(end-start);//截取文本-关键词高亮结束位置
            readLineVo.txt = line.substring(preStart,nextEnd);
            lineVos.add(readLineVo);
            DLog.info(start+"-"+end+":关键词: "+line.substring(start,end)+" :上下文= "+line.substring(preStart,nextEnd)+" : len="+(nextEnd-preStart+1)+": 高亮文本： "+readLineVo.txt.substring(readLineVo.colorStart,readLineVo.colorEnd));
        }
        return lineVos;
    }
    public FtsBookSearchVo queryBookKeywords(BookSearchParams searchParams){
        FtsBookSearchVo searchResult = new FtsBookSearchVo();
        List<TxtReadLineVo> datas = new ArrayList<>();
        searchResult.lines = datas;
        searchResult.offsetCid = 0L;
        if(JkhlUtils.isBlank(searchParams.keyword)){
            return searchResult;
        }
        Integer pageSize = 20;
        initBoolLocalData();
        Pattern keywordPa = WordTextUtils.normalKeywordToPattern(searchParams.keyword);
        for(FtsBookVo bookVo : ftsBookVos){
            if(bookVo.cid > searchParams.offsetCid){
                //String chapterText = bookVo.bcontList.stream().filter(d->JkhlUtils.notBlank(d.x)).map(d->d.x).collect(Collectors.joining(""));
                //List<TxtReadLineVo> chapterDatas = parseBookFtsLines(keywordPa,chapterText);
                List<TxtReadLineVo> chapterDatas = parseBookChapterFtsLinesPindex(keywordPa,bookVo.bcontList);
                for(int i = 0;i<chapterDatas.size();i++){
                    TxtReadLineVo item = chapterDatas.get(i);
                    item.cid = bookVo.cid;
                    if(i == 0){
                        item.cname = JkhlUtils.toStr(bookVo.cname).replaceAll(" ","");
                    }
                }
                datas.addAll(chapterDatas);
                searchResult.offsetCid = bookVo.cid;
                if(datas.size() >= pageSize){
                    break;
                }
            }
        }
        DLog.info("searchResult.lines.size=",searchResult.lines.size());
        return searchResult;
    }

    @Override
    protected void onDestroy() {
        DLog.info("页面销毁时移除fullSearchRunnable未执行的任务，避免内存泄漏");
        super.onDestroy();
        handler.removeCallbacks(fullSearchRunnable);
    }


}